﻿#region Header

/*
Behavioral Rating of Dancing Human Crowds based on Motion Patterns
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Using

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using Emgu.CV.Structure;
using Microsoft.Win32;
using paravili.Services;
using paravili.Steps;
using Sebarf.Diagnostics.Interfaces;
using Sebarf.ProcessChains;
using Sebarf.Profiles.WPF;
using Sebarf.Services.Impl;
using Sebarf.Utils.Interfaces;
using Utils;
using Visifire.Charts;

#endregion

namespace paravili.Ui {
	/// <summary>
	/// Displays the ui for an algorithm
	/// </summary>
	public partial class AlgorithmViewer {
		public AlgorithmViewer(string configurationFile, string profileId) {
			InitializeComponent();
			m_rootPath = Directory.GetCurrentDirectory();
			Init(profileId, configurationFile);
		}

		private void Init(string profileId, string configurationFile) {
			// get config file info
			var file = new FileInfo(configurationFile);
			configurationFile = file.Name;
			Check.AssertException(file.Exists, "config file must exist");
			// set root dictionary
			Directory.SetCurrentDirectory(file.Directory.FullName);

			// remeber profileId
			m_profileId = profileId;

			// create service locator
			m_serviceLocator = new ServiceRegistrationLocatorService();

			// register base services
			m_serviceLocator.RegisterService(new RootPathProviderService(m_rootPath));
			m_serviceLocator.RegisterService(new ObjectHierarchyFlattenerService());
			m_serviceLocator.RegisterService(new ProcessChainManagerService());
			m_serviceLocator.RegisterService(new DispatcherLocatorService(Dispatcher));

			// remeber manual services
			var manualServices = new List<Object>(m_serviceLocator.GetAllRegisteredServices<Object>());

			// load profile
			var handler = new WpfProfileLoadRequestHandler();
			handler.ProcessRequest(new WpfBasedProfileLoadRequest(profileId, configurationFile), m_serviceLocator);
			m_servicesByConfiguration =
				m_serviceLocator.GetAllRegisteredServices<Object>().Where(c => !manualServices.Contains(c)).Select(x => x).
					ToList();

			// set root dictionary
			Directory.SetCurrentDirectory(m_rootPath);

			// get process chains
			IProcessStep steps = m_serviceLocator.GetRegisteredService<ProcessChainManagerService>().GetProcessChains()[0];
			ProcessSteps.ItemsSource = new ProcessStepFlattener(steps).Steps;

			// get root step
			m_rootStep = steps as NextImageProvider;
			Check.ArgumentNotNull(m_rootStep, "RootStep is not a NextImageProvider ");

			// load images
			ImagesResults.ItemsSource = m_serviceLocator.GetRegisteredService<IImageLocatorService>().Images;
			// resolve dependencies within process step
			ServiceDependencySolver.ResolveDependencies(m_rootStep, m_serviceLocator);
			// resolve service dependencies
			var dependencyResolver = new ResolveDependenciesRequestHandler();
			foreach (object service in m_serviceLocator.GetRegisteredServices<Object>()) {
				dependencyResolver.ProcessRequest(new ResolveDependenciesRequest { ResolveMyReferences = service }, m_serviceLocator);
			}

			// resolve capture soruce dependencies
			foreach (var captureSource in m_serviceLocator.GetRegisteredService<ICaptureSourceLocatorService>().GetAvailableSources()) {
				dependencyResolver.ProcessRequest(new ResolveDependenciesRequest { ResolveMyReferences = captureSource }, m_serviceLocator);
			}

			// display capture sources
			var sourceProvider = m_serviceLocator.GetRegisteredService<ICaptureSourceLocatorService>();
			CaptureSources.SelectionChanged -= (CaptureSources_SelectionChanged);
			CaptureSources.ItemsSource =
				(from p in sourceProvider.GetAvailableSources() where p.IsAvailable select p).ToArray();
			CaptureSources.SelectionChanged += (CaptureSources_SelectionChanged);

			// display statistic values
			m_serviceLocator.GetRegisteredService<IStatisticValuesLocatorService>().StatisticsValues.CollectionChanged +=
				StatisticsValues_CollectionChanged;

			m_workerThread = CreateWorkerThread(m_rootStep);
			// start workerthread
			m_workerThread.Start();

			// create framerate chart
			m_framerateDataSeries = new DataSeries {
				LabelText = "Framerate",
				LabelLineStyle = LineStyles.Solid,
				LineStyle = LineStyles.Solid,
				LineThickness = 1,
				RenderAs = RenderAs.Line,
				LegendText = "Framerate"
			};
			for (int i = 0; i < StorageSize; i++) {
				m_framerateDataSeries.DataPoints.Add(new DataPoint { XValue = i, YValue = 0 });
			}
			m_chart.Series.Clear();
			m_chart.Series.Add(m_framerateDataSeries);

			ratingVisualizer.Init(m_serviceLocator);
			ratingVisualizerField.Init(m_serviceLocator);
		}

		private void StatisticsValues_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
			StatisticsValues.ItemsSource =
				(from o in m_serviceLocator.GetRegisteredService<IStatisticValuesLocatorService>().StatisticsValues.ToArray()
				 orderby o.Name
				 select o);
		}

		private Thread CreateWorkerThread(IProcessStep rootStep) {
			// declare worker thread
			return new Thread(delegate() {
				while (m_isRunning) {

					Dispatcher.Invoke(new ThreadStart(delegate {
						if (m_autoMode.IsChecked != true) {
							m_nextImage = true;
						}
					}), null);

					if (!m_nextImage) {
						Thread.Sleep(100);
						continue;
					}

					if (m_source == null) {
						Thread.Sleep(50);
						continue;
					}
					m_nextImage = false;
					rootStep.ProcessItem(m_source);
					Dispatcher.Invoke(new ThreadStart(delegate {
						if (DateTime.Now.Second == m_secondCounter) {
							m_counter++;
						}
						else {
							int index = (m_indexCounter++ % StorageSize);
							m_framerateDataSeries.DataPoints[index].YValue =
								m_counter;

							m_counter = 0;
							for (int i = 0; i < m_series.Length; i++) {
								m_series[i].DataPoints[index].YValue =
									m_entries[i].Value is double
										? (double)m_entries[i].Value
										: (int)m_entries[i].Value;
							}
							m_secondCounter = DateTime.Now.Second;
						}
					}));
				}
			});
		}

		#region Private Methods

		private void CaptureSources_SelectionChanged(object sender, SelectionChangedEventArgs e) {
			IList selectedItems = e.AddedItems;
			if (selectedItems.Count > 0) {
				object selectedItem = selectedItems[0];
				var newSource = selectedItem as ICaptureSource;
				Check.ArgumentNotNull(newSource, "newSource");
				if (newSource is IDisposableCaptureSource) {
					(newSource as IDisposableCaptureSource).DoInit();
				}
				m_workerThread.Abort();
				ICaptureSource oldSource = m_source;
				m_serviceLocator.GetRegisteredService<IResetHandlerService>().DoReset();
				m_source = newSource;
				if (oldSource != null && oldSource is IDisposableCaptureSource) {
					(oldSource as IDisposableCaptureSource).DoCleanUp();
				}
				m_workerThread = CreateWorkerThread(m_rootStep);
				m_workerThread.Start();
			}
		}

		protected override void OnClosing(CancelEventArgs e) {
			m_serviceLocator.GetRegisteredService<IResetHandlerService>().DoReset();
			m_isRunning = false;
			base.OnClosing(e);
			Process.GetCurrentProcess().Kill();
		}

		private void m_autoMode_Click(object sender, RoutedEventArgs e) {
		}

		private void Button_Click(object sender, RoutedEventArgs e) {
			m_nextImage = true;
		}

		private void StatisticsValues_SelectionChanged(object sender, SelectionChangedEventArgs e) {
			foreach (DataSeries serie in m_series) {
				m_chart.Series.Remove(serie);
				m_entries = new StatisticValueEntry[] { };
				m_series = new DataSeries[] { };
			}
			if (e.AddedItems == null) {
				m_entries = new StatisticValueEntry[] { };
			}
			else {
				var tmp = new List<StatisticValueEntry>();
				var tmp2 = new List<DataSeries>();
				foreach (StatisticValueEntry entry in StatisticsValues.SelectedItems) {
					if (entry.Value is double || entry.Value is int) {
						tmp.Add(entry);
						var serie = new DataSeries {
							LabelText = entry.Name,
							//=entry.Name,
							LabelLineStyle = LineStyles.Solid,
							LineStyle = LineStyles.Solid,
							LineThickness = 1,
							RenderAs = RenderAs.Line,
							LegendText = entry.Name
						};
						tmp2.Add(serie);
						for (int i = 0; i < StorageSize; i++) {
							serie.DataPoints.Add(new DataPoint { XValue = i, YValue = 0 });
						}
						m_chart.Series.Add(serie);
					}
				}
				m_entries = tmp.ToArray();
				m_series = tmp2.ToArray();
			}
		}

		private void m_saveConfiguration_Click(object sender, RoutedEventArgs e) {
			// reset process chain
			m_workerThread.Abort();
			ICaptureSource oldSource = m_source;
			m_serviceLocator.GetRegisteredService<IResetHandlerService>().DoReset();
			if (oldSource != null && oldSource is IDisposableCaptureSource) {
				(oldSource as IDisposableCaptureSource).DoCleanUp();
			}

			// Configure save file dialog box
			var dlg = new SaveFileDialog();
			dlg.FileName = "Algorithm"; // Default file name
			dlg.DefaultExt = ".xaml"; // Default file extension
			dlg.Filter = "Xaml File (.xaml)|*.xaml"; // Filter files by extension

			// Show save file dialog box
			bool? result = dlg.ShowDialog();

			// Process save file dialog box results
			if (result == true) {
				// Save document
				string filename = dlg.FileName;

				var res = new ResourceDictionary();

				string servicesContent = string.Empty;
				foreach (object service in m_servicesByConfiguration) {
					servicesContent = WriteObject(servicesContent, service);
				}
				string processStepContent = WriteObject(string.Empty, m_rootStep);
				string template = File.ReadAllText(m_rootPath + "\\Template.xaml");

				File.WriteAllText(filename, string.Format(template, servicesContent, processStepContent));

				Init("Model", filename);
				//XamlWriter.Save(new ImageProviderLocatorService(), stream);
				//stream.Close();
			}
		}

		private string WriteObject(string content, object obj) {
			if (obj == null) {
				return content;
			}
			if (obj is ValueType) {
				return content + obj;
			}
			object service = obj;
			Type t = service.GetType();
			content +=
				string.Format(
					"<{0}  xmlns=\"clr-namespace:{1};assembly={2}\"  xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">", t.Name,
					t.Namespace, t.Assembly.ManifestModule.Name.Replace(".dll", "").Replace(".exe", ""));

			PropertyInfo[] proptiers = service.GetType().GetProperties();
			foreach (PropertyInfo property in proptiers) {
				bool isConfigValue = false;
				foreach (Attribute attribute in property.GetCustomAttributes(true)) {
					if (attribute is ConfigurableValue) {
						isConfigValue = true;
					}
				}
				if (isConfigValue) {
					object value = property.GetValue(service, null);
					if (value == null) {
					}
					else if (value is IEnumerable && (!(value is string))) {
						if ((value is IList && (value as IList).Count == 0) || value is ICollection && (value as ICollection).Count == 0) {
							// do nothing
						}
						else {
							content += string.Format("<{1}.{0}>", property.Name, t.Name);
							foreach (object item in value as IEnumerable) {
								content = WriteObject(content, item);
							}
							content += string.Format("</{1}.{0}>", property.Name, t.Name);
						}
					}
					else {
						if (value is Lab || value is Bgr) {
							// TODO: reload colours
						}
						else if (value is System.Drawing.Point) {
							string v2 = string.Format("{0},{1}", ((System.Drawing.Point)value).X, ((System.Drawing.Point)value).Y);
							content += string.Format("<{2}.{0}><![CDATA[{1}]]></{2}.{0}>", property.Name, v2, t.Name);
						}
						else {
							content += string.Format("<{2}.{0}><![CDATA[{1}]]></{2}.{0}>", property.Name, value, t.Name);
						}
					}
				}
			}
			if (obj is IProcessStep) {
				var step = obj as IProcessStep;
				content = WriteObject(content, step.GetNextStep());
			}
			content += string.Format("</{0}>", t.Name);
			return content;
		}

		private void m_loadConfiguration_Click(object sender, RoutedEventArgs e) {
			// Configure open file dialog box
			var dlg = new OpenFileDialog();
			dlg.FileName = "Algorithm"; // Default file name
			dlg.DefaultExt = ".xaml"; // Default file extension
			dlg.Filter = "Xaml File (.xaml)|*.xaml"; // Filter files by extension

			// Show open file dialog box
			bool? result = dlg.ShowDialog();

			// Process open file dialog box results
			if (result == true) {
				// Open document
				string filename = dlg.FileName;

				// reset process chain
				m_workerThread.Abort();
				ICaptureSource oldSource = m_source;
				m_serviceLocator.GetRegisteredService<IResetHandlerService>().DoReset();
				if (oldSource != null && oldSource is IDisposableCaptureSource) {
					(oldSource as IDisposableCaptureSource).DoCleanUp();
				}

				// load new configuration
				Init("Model", filename);
			}
		}

		#endregion

		#region Private Fields

		private const int StorageSize = 30;
		private readonly string m_rootPath;
		private int m_counter;
		private StatisticValueEntry[] m_entries = new StatisticValueEntry[] { };
		private DataSeries m_framerateDataSeries;
		private int m_indexCounter;
		private bool m_isRunning = true;
		private bool m_nextImage = true;
		private string m_profileId;
		private NextImageProvider m_rootStep;
		private int m_secondCounter;
		private DataSeries[] m_series = new DataSeries[] { };
		private ServiceRegistrationLocatorService m_serviceLocator;
		private List<Object> m_servicesByConfiguration = new List<object>();
		private ICaptureSource m_source;
		private Thread m_workerThread;

		#endregion
	}
}